module nade.loader;

import std.algorithm;
import std.traits;

import nade.array;
import nade.ast;


class Loader(T = uint) {
	alias void delegate(ref T, AST, Object[string] state) DLoader;

	static {
		private {
			static if(__traits(hasMember, T, "install"))
			{
				bool					_installed = false;
			}

			DLoader[][uint]			_numbered;
			DLoader[][string]		_named;
		}

		void load(ref T dest, AST ast, Object[string] state)
		{
			static if(__traits(hasMember, T, "install"))
			{
				if(!_installed)
				{
					_installed = true;
					T.install;
				}
			}

			static if(is(T : Object)  &&  !is(BaseClassesTuple!(T)[0] == Object))
			{
				BaseClassesTuple!(T)[0] cdest = dest;
				Loader!(BaseClassesTuple!(T)[0]).load(cdest, ast, state);
			}

			foreach(k, node; ast)
			{
				auto p = k in _numbered;
				if(p !is null)
				{
					foreach(dg; *p)
						dg(dest, node, state);
				}
				else
				{
					p = node.label in _named;
					if(p !is null)
					{
						Object[string] substate = state.dup;
						substate[node.label] = dest;

						foreach(dg; *p)
							dg(dest, node, substate);
					}
				}
			}
		}

		void add(uint index, DLoader loader)
		{
			_numbered[index] ~= loader;
		}
		void add(string name, DLoader loader)
		{
			_named[name] ~= loader;
		}
		void add(uint[] indices, DLoader loader)
		{
			foreach(index; indices) add(index, loader);
		}
		void add(string[] names, DLoader loader)
		{
			foreach(name; names) add(name, loader);
		}
	}
}


void load(T)(ref T dest, AST ast, Object[string] state)
{
	Loader!(T).load(dest, ast, state);
}
